/**
 * 
 */
package multimedia.model;

import java.awt.Dimension;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Scanner;
import java.util.Vector;

import javax.management.modelmbean.InvalidTargetObjectTypeException;

import magick.ColorspaceType;
import multimedia.model.coding.HuffmanDecoder;
import multimedia.model.coding.HuffmanEncoder;
import multimedia.model.coding.LZW;
import multimedia.model.colorspace.ColorspaceInterface;
import multimedia.model.colorspace.DefaultColorspace;

/**
 * @author trigueros
 *
 */
public class Decoder
{
    private File binaryFile;
    
    private int predictorType;
    private int quantizerType;
    private int encoderType;
    
    public Decoder( File binaryFile )
    {
        this.binaryFile = binaryFile;
    }
    
    public ModifiedImage parseBinaryFile() throws InvalidTargetObjectTypeException
    {
        ModifiedImage parsedImage = null;
        try
        {
            parsedImage = new ModifiedImage();
            Scanner fileReader = new Scanner(binaryFile);
            
            String originalFilename = binaryFile.getName();
            int colorspace;
            
            // Determine the format of the file read
            if( originalFilename.endsWith("YIQ") )
            {
                colorspace = ColorspaceType.YIQColorspace;
                // Then it should be parsed with YIQ
            }
            else if( originalFilename.endsWith("RGB") )
            {
                colorspace = ColorspaceType.RGBColorspace;
                // Then it should be parsed with RGB
            }
            else
            {
                throw new InvalidTargetObjectTypeException();
            }
            
            parsedImage.setOriginalFilename(originalFilename);
            parsedImage.setColorspace(colorspace);
            
            // The first line has the dimensions
            String[] dimensionLine = fileReader.nextLine().split(" ");
            
            // Set the dimensions
            parsedImage.setImageSize( new Dimension( Integer.parseInt(dimensionLine[0]), 
                                                     Integer.parseInt(dimensionLine[1]) ) );
//            parsedImage.setRegionDimension(Integer.parseInt(dimensionLine[0]), Integer.parseInt(dimensionLine[1]) );
            
            /*
             * This is a big TODO, I don't know how we are going to go about determining what to do with the information
             * if you guys have any suggestions, this is where you would be putting it. I guess I'll lay out some footwork
             * then we can go from there
             */
            // TODO: Add decoding instructions
            // The second line has decoding instructions
            String[] encodingParameters = fileReader.nextLine().split(" ");
            
            // Set Quantization 
            quantizerType = Integer.parseInt(encodingParameters[0]);
            // There is no need to un-quantize, once the information is lost it is lost.
            
            // Set the Predictor and perform decoding
//            Predictor predictor = new Predictor(parsedImage, Integer.parseInt(encodingParameters[1]));
            predictorType = Integer.parseInt(encodingParameters[1]);
            
            // Set Encoding
            encoderType = Integer.parseInt(encodingParameters[2]);
            
            // Must decode here
            if( encoderType != 0 )
            {
//                String decodedString = fileReader.nextLine();
//                if( encoderType == LZW.LZW )
//                {
//                    LZW decoder = new LZW();
//                    decodedString = decoder.decode(decodedString);
//                }
//                else if( encoderType == 1)
//                {
//                    // Do Nothing for now
//                }
//                
                // Decode every component separately.
                String[] decodedString = new String[3];
                int i = 0;
                while( fileReader.hasNextLine() && i < decodedString.length )
                {
                    String currentLine = fileReader.nextLine();
                    if( encoderType == LZW.LZW )
                    {
                        LZW decoder = new LZW();
                        decodedString[i++] = decoder.decode(currentLine) + " ";
                    }
                    else if( encoderType == HuffmanEncoder.HUFF )
                    {
                        String[] arguments = currentLine.split(" ");
                        int inputLength = Integer.parseInt(arguments[0]);
                        int tableSize = Integer.parseInt(arguments[1]);
                        Hashtable<Character, String> table = new Hashtable<Character, String>();
                        ArrayList<Byte> binaryEncodedData = new ArrayList<Byte>();
                        
                        // Read the table
                        for( int t = 0; t < tableSize; t++ )
                        {
                            currentLine = fileReader.nextLine();
                            String[] tableEntry;
                            
                            if( currentLine.charAt(0) == ' ' )
                            {
                                tableEntry = new String[]{" ", currentLine.substring(2)};
                            }
                            else
                                 tableEntry = currentLine.split(" ");
                            
                            table.put(tableEntry[0].charAt(0), tableEntry[1]);
                        }
                        
                        // Read the Byte array
                        Scanner byteIterator = new Scanner( fileReader.nextLine() );
                        while( byteIterator.hasNext() )
                            binaryEncodedData.add( (byte) Integer.parseInt(byteIterator.next()) );
                        
                        // Finally decode
                        HuffmanDecoder decoder = new HuffmanDecoder();
                        decodedString[i++] = decoder.decode( binaryEncodedData,table, inputLength ) + " ";
                    }
                }
                
                fileReader = new Scanner(PixelCoordinate.mergeComponentStrings(decodedString));
            }
            
            
            // Set Headers
            parsedImage.setBinaryHeader(quantizerType + " " + predictorType + " " + encoderType);
            
            Vector<ColorspaceInterface> binaryFileVector = new Vector<ColorspaceInterface>();
            while( fileReader.hasNext() )
            {
                // Create dummy pixel holder 
                DefaultColorspace currentPixel = new DefaultColorspace();
                
                // Get the next three values from the file
                currentPixel.setFirstComponent(  (float) Double.parseDouble( fileReader.next() ) );
                currentPixel.setSecondComponent( (float) Double.parseDouble( fileReader.next() ) );
                currentPixel.setThirdComponent(  (float) Double.parseDouble( fileReader.next() ) );
                
                // Insert that new pixel into the vector
                binaryFileVector.add( currentPixel );
            }
            
            // Finally set the image pixels and the region pixels 
            parsedImage.setImagePixelHolder( new PixelCoordinate(binaryFileVector, parsedImage.getImageSize().width ) );
//            parsedImage.setRegionPixelHolder( new PixelCoordinate(binaryFileVector, parsedImage.getImageSize().width ) );
            
            
            // TODO: Doing this cause there is a bug in the way we write the pixels
            // TODO: This might affect something, might need to make a setter for regionPixels
//            parsedImage.selectRegion(new Point(0,0), new Point(parsedImage.getImageSize().width, parsedImage.getImageSize().height));
            
        } catch ( NumberFormatException e )
        {
            e.printStackTrace();
        } catch ( FileNotFoundException e )
        {
            e.printStackTrace();
        }
        
        return parsedImage;
    }

    /**
     * @return the predictorType
     */
    public int getPredictorType()
    {
        return predictorType;
    }

    /**
     * @param predictorType the predictorType to set
     */
    public void setPredictorType( int predictorType )
    {
        this.predictorType = predictorType;
    }

    /**
     * @return the quavtizerType
     */
    public int getQuantizerType()
    {
        return quantizerType;
    }

    /**
     * @param quantizerType the quavtizerType to set
     */
    public void setQuantizerType( int quantizerType )
    {
        this.quantizerType = quantizerType;
    }

    /**
     * @return the encoderType
     */
    public int getEncoderType()
    {
        return encoderType;
    }

    /**
     * @param encoderType the encoderType to set
     */
    public void setEncoderType( int encoderType )
    {
        this.encoderType = encoderType;
    }
}
